#include "common.h"
#include "level.h"
#include "object.h"
#include "actormanager.h"
#include "actor.h"
#include "pawn.h"

#include "numbers.h"

#include "asset.h"
#include "mesh.h"
#include "effect.h"
#include "body.h"

#include "scene.h"
#include "scenemanager.h"

#ifdef _DEBUG
#include "debugdialog.h"
#endif

#include "collada.h"
#include "colladawrapper.h"

#include "playstate.h"

#ifdef _DEBUG
#define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
#define new DEBUG_NEW
#endif

#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL		0x020A
#endif

PlayState::PlayState()
:	GameState( GM_PLAY )
,	_pLevelData( NULL )
,	_pTimer( NULL )
,	_isIntroFinished( false )
,	_isLevelCleared( false )
{
}

PlayState::~PlayState()
{
	if( _pLevelData )
	{
		delete _pLevelData;
		_pLevelData = NULL;
	}
	if( _pScene )
	{
		delete _pScene;
		_pScene = NULL;
	}
}

bool PlayState::CreateNewLevel( int level )
{
	if( _pLevelData )
	{
		delete _pLevelData;
		_pLevelData = NULL;
	}
	_pLevelData = new Level( level );
	if( _pLevelData == NULL )
	{
		return false;
	}

	WCHAR wstr[50];
	wsprintf( wstr, L"Level%03d", level );

	LEVEL_DESC desc;
	desc.nGrid				= GetPrivateProfileInt( wstr, L"NumberOfGrids", 0, gLevelFilePath );
	desc.nBranch			= GetPrivateProfileInt( wstr, L"NumberOfBranches", 0, gLevelFilePath );
	desc.directionalWeight	= GetPrivateProfileInt( wstr, L"EndPointDirectionalPercentage", 0, gLevelFilePath ) / 100.0f;
	desc.straightnessWeight	= GetPrivateProfileInt( wstr, L"StraightnessPercentage", 0, gLevelFilePath ) / 100.0f;
	desc.restrictTime		= GetPrivateProfileInt( wstr, L"RestrictTime", 0, gLevelFilePath );

	_pLevelData->Initialize( desc );

	return true;
}

bool PlayState::Initialize( void* info )
{
	ActorManager::Register( "CameraActor", "./media/class/CameraActor.lua" );
	ActorManager::Register( "Cube", "./media/class/Cube.lua" );
	ActorManager::Register( "Plane", "./media/class/Plane.lua" );

	int level = ( info == NULL )? 1 : 1;
	if( CreateNewLevel( level ) == false )
	{
		assert( false );
		return false;
	}

	char lvBuffer[6] = {0,};
	sprintf_s( lvBuffer, sizeof( lvBuffer ), "lv%03d", level );

	std::string path( "./media/scene/" );
	std::string ext( ".lua" );
	std::string introSceneName = std::string( "scene_" ) + lvBuffer + "_intro";
	std::string mainSceneName = std::string( "scene_" ) + lvBuffer + "_main";
	
	//Core::GetSceneManager().Register( introSceneName, path + introSceneMame + ext );
	Core::GetSceneManager().Register( mainSceneName, path + mainSceneName + ext );
	
	//_pScene = Core::GetSceneManager().GetScene( introSceneName );
	//Core::GetSceneManager().SetCurrentScene( introSceneName );
	_pScene = Core::GetSceneManager().GetScene( mainSceneName );
	Core::GetSceneManager().SetCurrentScene( mainSceneName );

	Actor* pPlane = _pScene->FindActor( "Background" );
	pPlane->SetPosition( D3DXVECTOR3( 0, -10.0f, -45.0f ) );
	pPlane->SetParam( "planeSize", _pLevelData->GetGridCount() / 10.0f );

	Actor* pPlayer = _pScene->FindActor( "Player" );
	pPlayer->Call< void >( "SetCubeColor", D3DXVECTOR3( 1.0f, 1.0f, 0 ) );
	pPlayer->Call< void >( "SetCubeSizeOffset", 0.5f );
	Point playerPos = _pLevelData->GetPlayerPos();
	pPlayer->SetPosition( D3DXVECTOR3( 10.0f*(playerPos.y-4.5f), 0, 10.0f*(playerPos.x-4.5f) ) );

	// Add cubes to scene
	_pLevelData->SetVisibleFace( _pScene->GetViewCamera()->GetViewVector() );
	_pLevelData->SetVisibleMap( _pLevelData->GetVisibleFace() );
	char* vmap = _pLevelData->GetVisibleMap();
	char cubeBuffer[9] = {0,};
	for( UINT r= 0; r < _pLevelData->GetGridCount(); ++r )
	{
		for( UINT c = 0; c < _pLevelData->GetGridCount(); ++c )
		{
			sprintf_s( cubeBuffer, sizeof( cubeBuffer ), "r%03dc%03d", r, c );
			Pawn* pCube = Pawn::Create( "Cube", cubeBuffer );
			pCube->SetPosition( D3DXVECTOR3( 10.0f*(r-4.5f), 0, 10.0f*(c-4.5f) ) );

			float offset = ( vmap[ c*_pLevelData->GetGridCount()+r ] / 6.0f ) * 0.6f;
			pCube->Call< void >( "SetCubeSizeOffset", offset );

			if( _pLevelData->GetStartPos() == Point( r, c ) )
			{
				pCube->Call< void >( "SetCubeSizeOffset", 0 );
				pCube->Call< void >( "SetCubeColor", D3DXVECTOR3( 1.0f, 0, 0 ) );
			}

			if( _pLevelData->GetEndPos() == Point( r, c ) )
			{
				pCube->Call< void >( "SetCubeSizeOffset", 0.5f );
				pCube->Call< void >( "SetCubeColor", D3DXVECTOR3( 0, 0, 1.0f ) );
			}

			_pScene->AddActor( pCube );
		}
	}

	Numbers* lv = ( Numbers* )_pScene->FindUI( "LevelUI" );
	if( lv )
	{
		char buf[5] = {0,};
		sprintf_s( buf, sizeof( buf ), "%d", _pLevelData->GetLevel() );
		lv->SetNumbers( buf );
	}

	Numbers* timer = ( Numbers* )_pScene->FindUI( "TimerUI" );
	if( timer )
	{
		_pTimer = timer;
		_pTimer->SetKerningRatio( 0.9f );
	}

#ifdef _DEBUG
	DebugDialog* dlg = Core::GetInstance().GetDebugDialog();
	InvalidateRect( dlg->GetTabCtrlHandle(), NULL, TRUE );
#endif

	return true;
}

void PlayState::ChangeState( GameState** currentState, eGameMode nextState )
{
	char lvBuffer[6] = {0,};
	sprintf_s( lvBuffer, sizeof( lvBuffer ), "lv%03d", _pLevelData->GetLevel() );

	std::string mainSceneName = std::string( "scene_" ) + lvBuffer + "_main";
	std::string introSceneName = std::string( "scene_" ) + lvBuffer + "_intro";

	Core::GetSceneManager().Unregister( mainSceneName );
	Core::GetSceneManager().Unregister( introSceneName );
}

LRESULT PlayState::MessageHandler( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
#ifdef _DEBUG
	DebugDialog* pDlg = Core::GetInstance().GetDebugDialog();
#endif

	Camera* pViewCam = Core::GetInstance().GetSceneManager().GetCurrentScene()->GetViewCamera();

	switch( msg )
	{
	case WM_DESTROY :
		Core::GetInstance().PrepareToQuit();
		break;
	case WM_LBUTTONDOWN :
		pViewCam->OnLButtonPushed();
		break;
	case WM_LBUTTONUP :
		pViewCam->OnLButtonReleased();
		break;
	case WM_MOUSEMOVE :
		{
			static int prevX = LOWORD( lParam );
			static int prevY = HIWORD( lParam );

			int x = LOWORD( lParam );
			int y = HIWORD( lParam );
			pViewCam->OnMouseMoved( prevX-x, prevY-y );

			eFace prev = _pLevelData->GetVisibleFace();
			_pLevelData->SetVisibleFace( pViewCam->GetViewVector() );
			eFace next = _pLevelData->GetVisibleFace();

			if( prev != next )
			{
				_pLevelData->SetVisibleMap( next );
				Scene::ActorListIter it = _pScene->GetActorList().begin();
				for( ; it != _pScene->GetActorList().end(); ++it )
				{
					if( ( *it )->GetType() != "Cube" ||
						( *it )->GetName() == "Player" )
					{
						continue;
					}

					int r = 0;
					int c = 0;
					sscanf_s( ( *it )->GetName().c_str(), "r%dc%d", &r, &c );
					char* vmap = _pLevelData->GetVisibleMap();
					float offset = ( vmap[ c*_pLevelData->GetGridCount()+r ] / 6.0f ) * 0.6f;

					if( _pLevelData->GetPlayerPos() == Point( r, c ) )
					{
						( *it )->Call< void >( "SetCubeSizeOffset", 0 );
					}
					else if( _pLevelData->GetStartPos() == Point( r, c ) )
					{
						( *it )->Call< void >( "SetCubeSizeOffset", 0.5f );
					}
					else if( _pLevelData->GetEndPos() == Point( r, c ) )
					{
						( *it )->Call< void >( "SetCubeSizeOffset", 0.5f );
					}
					else
					{
						( *it )->Call< void >( "SetCubeSizeOffset", offset );
					}
				}
			}

			prevX = x;
			prevY = y;

		}
		break;
	case WM_MOUSEWHEEL :
		pViewCam->OnWheelScrolled( ( short )( HIWORD( wParam ) ) );
		break;
	case WM_KEYDOWN :
		{
			Point prev = _pLevelData->GetPlayerPos();
			switch( wParam )
			{
			case VK_UP :
				_pLevelData->OnArrowKeyPressed( "up", pViewCam->GetViewVector() );
				break;
			case VK_DOWN :
				_pLevelData->OnArrowKeyPressed( "down", pViewCam->GetViewVector() );
				break;
			case VK_LEFT :
				_pLevelData->OnArrowKeyPressed( "left", pViewCam->GetViewVector() );
				break;
			case VK_RIGHT :
				_pLevelData->OnArrowKeyPressed( "right", pViewCam->GetViewVector() );
				break;
			}
			Point current = _pLevelData->GetPlayerPos();

			if( prev == current )
			{
			}
			else
			{
				Actor* pPlayer = _pScene->FindActor( "Player" );
				pPlayer->SetPosition( D3DXVECTOR3( 10.0f*(current.x-4.5f), 0, 10.0f*(current.y-4.5f) ) );

				char* vmap = _pLevelData->GetVisibleMap();
				float offset = ( vmap[ prev.y*_pLevelData->GetGridCount()+prev.x ] / 6.0f ) * 0.6f;

				char buf[9] = {0,};
				sprintf_s( buf, sizeof( buf ), "r%03dc%03d", prev.x, prev.y );
				Actor* pCube = _pScene->FindActor( buf );
				pCube->Call< void >( "SetCubeSizeOffset", offset );

				sprintf_s( buf, sizeof( buf ), "r%03dc%03d", current.x, current.y );
				pCube = _pScene->FindActor( buf );
				pCube->Call< void >( "SetCubeSizeOffset", 0 );
			}
		}
		break;
#ifdef _DEBUG
	case WM_MOVE :
		if( pDlg )
		{
			pDlg->ResetDialogPos();
		}
		break;
#endif
	}
	return DefWindowProc( hWnd, msg, wParam, lParam );
}

void PlayState::Release()
{
	if( _pLevelData )
	{
		delete _pLevelData;
		_pLevelData = NULL;
	}

	//delete this;
}

void PlayState::RefreshTimer()
{
	if( _pTimer == NULL )
	{
		return;
	}

	D3DXVECTOR3 c1 = D3DXVECTOR3( 200, 200, 200 );
	D3DXVECTOR3 c2 = D3DXVECTOR3( 255, 0, 102 );

	int countdown = 10;
	float elasped = _pLevelData->GetElaspedTime();
	int restrict = _pLevelData->GetRestrictTime();

	int min = static_cast< int >( ( restrict-elasped+1 ) / 60.0f );
	int sec = static_cast< int >( restrict-elasped+1 ) % 60;

	char buf[10] = {0,};
	sprintf_s( buf, sizeof( buf ), "%02d:%02d", min, sec );
	_pTimer->SetNumbers( buf );

	DWORD color;
	if( restrict - (int)elasped > countdown )
	{
		color = D3DCOLOR_ARGB( 255, (int)c1.x, (int)c1.y, (int)c1.z );
	}
	else
	{
		float weight = ( restrict - elasped ) / static_cast< float >( countdown );
		color = D3DCOLOR_ARGB( 
			255, 
			static_cast< int >( weight*c1.x + ( 1-weight )*c2.x ), 
			static_cast< int >( weight*c1.y + ( 1-weight )*c2.y ), 
			static_cast< int >( weight*c1.z + ( 1-weight )*c2.z ) );
	}
	_pTimer->SetColor( color );
}

void PlayState::ReloadLevel( int offset )
{
	CreateNewLevel( _pLevelData->GetLevel() + offset );
}

void PlayState::Run( GameState** currentState, float time )
{
	if( _isIntroFinished == false )
	{
		Intro( time );
		_isIntroFinished = true;
	}

	_pLevelData->UpdateTime( time );
	RefreshTimer();
	
#ifdef _DEBUG
	Core::GetInstance().GetDebugDialog()->SetLevelInfos( _pLevelData );
#endif

	Scene::ActorList& list = _pScene->GetActorList();
	Scene::ActorListIter it = list.begin();
	for( ; it != list.end(); ++it )
	{
		( *it )->Update( time );
	}

	if( _pScene != NULL )
	{
		_pScene->DrawScene( time );
	}

	if( _pLevelData->GetPlayerPos() == _pLevelData->GetEndPos() )
	{
		MessageBox( NULL, L"Level Cleared!", L"Result", 0 );
		Core::GetInstance().PrepareToQuit();
	}

	if( ( _pLevelData->GetRestrictTime() ) - _pLevelData->GetElaspedTime() < 0 )
	{
		MessageBox( NULL, L"Game Over!", L"Result", 0 );
		Core::GetInstance().PrepareToQuit();
	}

	/*
	if( _isLevelCleared == true )
	{
	}
	*/
}


